{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "# 学习RAG：逐步测试配置\n",
        "## 一个具有增强评估的教育性端到端流水线\n",
        "\n",
        "本notebook设计为学习项目，用于理解不同设置如何影响检索增强生成（RAG）系统。我们将使用**Nebius AI API**逐步构建和测试流水线。\n",
        "\n",
        "**我们将学习的内容：**\n",
        "*   文本分块（`chunk_size`、`chunk_overlap`）如何影响RAG系统检索的内容。\n",
        "*   检索文档数量（`top_k`）如何影响提供给LLM的上下文。\n",
        "*   三种常见RAG策略的区别（简单RAG、查询重写、重新排序）。\n",
        "*   如何使用LLM（如Nebius AI）通过多个指标自动评估生成答案的质量：**忠实度**、**相关性**和与真实答案的**语义相似性**。\n",
        "*   如何将这些指标组合成平均分数以便于比较。\n",
        "\n",
        "我们将专注于理解*为什么*执行每个步骤，并清楚地观察结果，提供详细的解释和注释代码。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "### 目录\n",
        "1.  **设置：安装库**：获取必要的工具。\n",
        "2.  **设置：导入库**：将工具导入我们的工作区。\n",
        "3.  **配置：设置我们的实验**：定义API详细信息、模型、评估提示和要测试的参数。\n",
        "4.  **输入数据：知识来源和我们的问题**：定义RAG系统将从中学习的文档和我们将询问的问题。\n",
        "5.  **核心组件：文本分块函数**：创建将文档分解为较小片段的函数。\n",
        "6.  **核心组件：连接到Nebius AI**：建立使用Nebius模型的连接。\n",
        "7.  **核心组件：余弦相似度函数**：创建测量文本之间语义相似性的函数。\n",
        "8.  **实验：遍历配置**：测试不同设置的主循环。\n",
        "    *   8.1 处理分块配置（分块、嵌入、索引）\n",
        "    *   8.2 测试`top_k`值的RAG策略\n",
        "    *   8.3 运行和评估单个RAG策略（包括相似性）\n",
        "9.  **分析：查看结果**：使用Pandas组织和显示结果。\n",
        "10. **结论：我们学到了什么？**：反思发现和潜在的下一步。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "### 1. 设置：安装库\n",
        "\n",
        "首先，我们需要安装此notebook所需的Python包。\n",
        "- `openai`：与Nebius API交互（使用OpenAI兼容接口）。\n",
        "- `pandas`：用于创建和管理数据表（DataFrames）。\n",
        "- `numpy`：用于数值操作，特别是向量（嵌入）。\n",
        "- `faiss-cpu`：用于对向量进行高效相似性搜索（检索部分）。\n",
        "- `ipywidgets`、`tqdm`：用于在Jupyter中显示进度条。\n",
        "- `scikit-learn`：用于计算余弦相似度。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 安装库（如果需要，只运行此单元格一次）\n",
        "# !pip install openai pandas numpy faiss-cpu ipywidgets tqdm scikit-learn\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "**记住！** 安装完成后，您可能需要**重启内核**（或运行时）让Jupyter/Colab识别新安装的包。在菜单中查找此选项（例如，'Kernel' -> 'Restart Kernel...' 或 'Runtime' -> 'Restart Runtime'）。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "### 2. 设置：导入库\n",
        "\n",
        "安装库后，我们将它们导入到Python环境中，使其函数可用。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "import os                     # 用于访问环境变量（如API密钥）\n",
        "import time                   # 用于计时操作\n",
        "import re                     # 用于正则表达式（文本清理）\n",
        "import warnings               # 用于控制警告消息\n",
        "import itertools              # 用于轻松创建参数组合\n",
        "import getpass                # 用于在未设置API密钥时安全提示\n",
        "\n",
        "import numpy as np            # 用于向量操作的数值库\n",
        "import pandas as pd           # 用于表格（DataFrames）的数据操作库\n",
        "import faiss                  # 用于快速向量相似性搜索的库\n",
        "from openai import OpenAI     # 用于Nebius API交互的客户端库\n",
        "from tqdm.notebook import tqdm # 用于显示进度条的库\n",
        "from sklearn.metrics.pairwise import cosine_similarity # 用于计算相似性分数\n",
        "\n",
        "# 为更好的可读性配置Pandas DataFrames的显示选项\n",
        "pd.set_option('display.max_colwidth', 150) # 在表格单元格中显示更多文本内容\n",
        "pd.set_option('display.max_rows', 100)     # 在表格中显示更多行\n",
        "warnings.filterwarnings('ignore', category=FutureWarning) # 抑制特定的非关键警告\n",
        "\n",
        "print(\"库导入成功！\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "### 3. 配置：设置我们的实验\n",
        "\n",
        "在这里，我们直接将实验的所有设置和参数定义为Python变量。这使得在一个地方查看和修改配置变得容易。\n",
        "\n",
        "**关键配置区域：**\n",
        "*   **Nebius API详细信息：** 连接到Nebius AI的凭据和模型标识符。\n",
        "*   **LLM设置：** 控制语言模型在答案生成期间行为的参数（例如，控制创造性的`temperature`）。\n",
        "*   **评估提示：** 当LLM充当忠实度和相关性评估器时给出的具体指令（提示）。\n",
        "*   **调优参数：** 我们想要系统测试的块大小、重叠和检索`top_k`的不同值。\n",
        "*   **重新排序设置：** 模拟重新排序策略的配置。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# --- NebiusAI API配置 ---\n",
        "# 最佳实践是将API密钥存储为环境变量，而不是硬编码。\n",
        "# 在此处提供您的实际密钥或将其设置为环境变量\n",
        "NEBIUS_API_KEY = os.getenv('NEBIUS_API_KEY', None)  # 从环境变量加载API密钥\n",
        "if NEBIUS_API_KEY is None:\n",
        "    print(\"警告：NEBIUS_API_KEY未设置。请在环境变量中设置或直接在代码中提供。\") \n",
        "NEBIUS_BASE_URL = \"https://api.studio.nebius.com/v1/\" \n",
        "NEBIUS_EMBEDDING_MODEL = \"BAAI/bge-multilingual-gemma2\"  # 将文本转换为向量嵌入的模型\n",
        "NEBIUS_GENERATION_MODEL = \"deepseek-ai/DeepSeek-V3\"    # 用于生成最终答案的LLM\n",
        "NEBIUS_EVALUATION_MODEL = \"deepseek-ai/DeepSeek-V3\"    # 用于评估生成答案的LLM\n",
        "\n",
        "# --- 文本生成参数（用于RAG答案生成） ---\n",
        "GENERATION_TEMPERATURE = 0.1  # 较低的值（例如0.1-0.3）使输出更集中和确定，适合基于事实的答案。\n",
        "GENERATION_MAX_TOKENS = 400   # 生成答案中的最大令牌数（大致为单词/子单词）。\n",
        "GENERATION_TOP_P = 0.9        # 核采样参数（温度的替代品，通常默认值即可）。\n",
        "\n",
        "# --- 评估提示（评估器LLM的指令） ---\n",
        "# 忠实度: 答案是否忠于提供的上下文?\n",
        "FAITHFULNESS_PROMPT = \\\"\\\"\\\"\n",
        "系统: 您是一个客观的评估者. 评估AI响应相对于真实答案的忠实度, 仅将真实答案中存在的信息视为真实依据.\n",
        "忠实度衡量AI响应如何准确反映真实答案中的信息, 而不添加不支持的事实或与其矛盾.\n",
        "严格使用0.0到1.0之间的浮点数评分, 基于此标准:\n",
        "- 0.0: 完全不忠实, 矛盾或编造信息.\n",
        "- 0.1-0.4: 忠实度低, 有重大不准确性或不支持的声明.\n",
        "- 0.5-0.6: 部分忠实, 但有明显的不准确性或遗漏.\n",
        "- 0.7-0.8: 大部分忠实, 只有轻微的不准确性或措辞差异.\n",
        "- 0.9: 非常忠实, 轻微措辞差异但语义一致.\n",
        "- 1.0: 完全忠实, 准确反映真实答案.\n",
        "仅回答数字分数.\n",
        "\n",
        "用户:\n",
        "查询: {question}\n",
        "AI响应: {response}\n",
        "真实答案: {true_answer}\n",
        "分数:\\\"\\\"\\\"\n",
        "\n",
        "# 相关性: 答案是否直接回答用户的查询?\n",
        "RELEVANCY_PROMPT = \\\"\\\"\\\"\n",
        "系统: 您是一个客观的评估者. 评估AI响应对特定用户查询的相关性.\n",
        "相关性衡量响应如何直接回答用户的问题, 避免不必要或偏离主题的信息.\n",
        "严格使用0.0到1.0之间的浮点数评分, 基于此标准:\n",
        "- 0.0: 完全不相关.\n",
        "- 0.1-0.4: 相关性低, 涉及不同主题或错过核心问题.\n",
        "- 0.5-0.6: 部分相关, 仅回答查询的一部分或切线相关.\n",
        "- 0.7-0.8: 大部分相关, 涉及查询的主要方面, 但可能包含次要的不相关细节.\n",
        "- 0.9: 高度相关, 直接回答查询, 额外信息最少.\n",
        "- 1.0: 完全相关, 直接完整回答确切的问题.\n",
        "仅回答数字分数.\n",
        "\n",
        "用户:\n",
        "查询: {question}\n",
        "AI响应: {response}\n",
        "分数:\\\"\\\"\\\"\n",
        "\n",
        "# --- 要调优的参数（实验变量） ---\n",
        "CHUNK_SIZES_TO_TEST = [150, 250]    # 要实验的块大小（以单词为单位）列表。\n",
        "CHUNK_OVERLAPS_TO_TEST = [30, 50]   # 要实验的块重叠（以单词为单位）列表。\n",
        "RETRIEVAL_TOP_K_TO_TEST = [3, 5]   # 要测试的'k'值（要检索的块数）列表。\n",
        "\n",
        "# --- 重新排序配置（仅用于重新排序策略） ---\n",
        "RERANK_RETRIEVAL_MULTIPLIER = 3 # 对于模拟重新排序：最初检索K * 乘数块。\n",
        "\n",
        "# --- 验证API密钥 --- \n",
        "print(\"--- 配置检查 --- \")\n",
        "print(f\"尝试从环境变量'NEBIUS_API_KEY'加载Nebius API密钥...\")\n",
        "if not NEBIUS_API_KEY:\n",
        "    print(\"在环境变量中未找到Nebius API密钥。\")\n",
        "    # 如果未找到密钥，安全地提示用户。\n",
        "    NEBIUS_API_KEY = getpass.getpass(\"请输入您的Nebius API密钥：\")\n",
        "else:\n",
        "    print(\"成功从环境变量加载Nebius API密钥。\")\n",
        "\n",
        "# 打印关键设置摘要以供验证\n",
        "print(f\"模型：嵌入='{NEBIUS_EMBEDDING_MODEL}'，生成='{NEBIUS_GENERATION_MODEL}'，评估='{NEBIUS_EVALUATION_MODEL}'\")\n",
        "print(f\"要测试的块大小：{CHUNK_SIZES_TO_TEST}\")\n",
        "print(f\"要测试的重叠：{CHUNK_OVERLAPS_TO_TEST}\")\n",
        "print(f\"要测试的Top-K值：{RETRIEVAL_TOP_K_TO_TEST}\")\n",
        "print(f\"生成温度：{GENERATION_TEMPERATURE}，最大令牌：{GENERATION_MAX_TOKENS}\")\n",
        "print(\"配置就绪。\")\n",
        "print(\"-\" * 25)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "### 4. 输入数据：知识来源和我们的问题\n",
        "\n",
        "每个RAG系统都需要一个知识库来获取信息。在这里，我们定义：\n",
        "*   `corpus_texts`：字符串列表，其中每个字符串是包含信息的文档（在本例中，关于可再生能源）。\n",
        "*   `test_query`：我们希望RAG系统使用`corpus_texts`回答的具体问题。\n",
        "*   `true_answer_for_query`：基于`corpus_texts`中可用信息精心制作的'真实'答案。这对于准确评估忠实度和语义相似性至关重要。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 我们的知识库：关于可再生能源的文本文档列表\n",
        "corpus_texts = [\n",
        "    \"太阳能使用光伏板或CSP系统。光伏直接将阳光转换为电力。CSP使用镜子加热流体驱动涡轮机。它是清洁的但随天气/时间变化。存储（电池）是一致性的关键。\", # 文档 0\n",
        "    \"风能使用风力发电场中的涡轮机。它是可持续的，运营成本低。风速变化，选址可能具有挑战性（视觉/噪音）。海上风力更强更一致。\", # 文档 1\n",
        "    \"水力发电使用流动的水，通常通过大坝旋转涡轮机。可靠的大规模发电，具有防洪/蓄水效益。大型水坝危害生态系统并迫使社区搬迁。径流式规模较小，干扰较少。\", # 文档 2\n",
        "    \"地热能通过蒸汽/热水驱动涡轮机使用地球热量。24/7一致供电，占地面积小。初期钻井成本高，站点在地理上有限。\", # 文档 3\n",
        "    \"生物质能来自有机物（木材、作物、废料）。直接燃烧或转化为生物燃料。利用废料，提供可调度电力。需要可持续采购。燃烧释放排放物（如果通过再生长平衡则碳中性）。\" # 文档 4\n",
        "]\n",
        "\n",
        "# 我们将向RAG系统询问的问题\n",
        "test_query = \"比较太阳能与水力发电的一致性和环境影响。\"\n",
        "\n",
        "# !!! 关键：'真实答案'必须仅从上述corpus_texts推导出来 !!!\n",
        "# 这是我们评估的真实标准。\n",
        "true_answer_for_query = \"太阳能的一致性随天气和时间变化，需要像电池这样的存储。水力发电通常是可靠的，但大型水坝对生态系统和社区有重大环境影响，不像太阳能的主要影响是面板的土地使用。\"\n",
        "\n",
        "print(f\"将{len(corpus_texts)}个文档加载到我们的语料库中。\")\n",
        "print(f\"测试查询：'{test_query}'\")\n",
        "print(f\"用于评估的参考（真实）答案：'{true_answer_for_query}'\")\n",
        "print(\"输入数据已准备好。\")\n",
        "print(\"-\" * 25)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "### 结论：我们学到了什么？\n",
        "\n",
        "我们已经成功构建并执行了一个端到端的流水线，用于实验各种RAG配置并使用Nebius AI平台上的多个指标评估其性能。\n",
        "\n",
        "通过检查上面的结果表和最佳配置摘要，我们可以获得特定于*我们选择的语料库、查询和模型*的见解。\n",
        "\n",
        "**反思要点：**\n",
        "\n",
        "*   **分块影响：** 特定的`chunk_size`或`overlap`是否倾向于产生更好的平均分数？考虑为什么较小的块可能更好地捕获特定事实，而较大的块可能提供更多上下文。重叠似乎如何影响结果？\n",
        "*   **检索数量(`top_k`)：** 增加`top_k`如何影响分数？检索更多块是否总是导致更好的答案，还是有时引入噪音或不相关信息，可能降低忠实度或相似性？\n",
        "*   **策略比较：** '查询重写'或'重新排序（模拟）'策略是否在平均分数方面比'简单RAG'提供一致的优势？潜在的改进是否足以证明额外步骤的合理性（例如，重写的额外LLM调用，重新排序的更大初始检索）？\n",
        "*   **评估指标：** \n",
        "    *   查看'最佳答案'并将其与`true_answer_for_query`比较。各个分数（忠实度、相关性、相似性）是否似乎反映您感知的质量？\n",
        "    *   高相似性是否总是与高忠实度相关？答案能否相似但不忠实，或忠实但不相似？ \n",
        "    *   与更客观的余弦相似性相比，您对自动化LLM评估（忠实度、相关性）的可靠性感觉如何？LLM评估的潜在限制是什么（例如，对提示措辞的敏感性、模型偏见）？\n",
        "*   **整体性能：** 任何配置是否达到了近乎完美的平均分数？是什么阻止了完美分数（例如，源文档的限制、语言中的固有歧义、不完美的检索）？\n",
        "\n",
        "**关键要点：** 优化RAG系统是一个迭代过程。最佳配置通常严重依赖于特定的数据集、用户查询的性质、选择的嵌入和LLM模型以及评估标准。像本notebook中遵循的系统实验过程对于找到在特定用例中表现良好的设置至关重要。\n",
        "\n",
        "**潜在的下一步和进一步探索：**\n",
        "\n",
        "*   **扩展测试参数：** 尝试更广泛的`chunk_size`、`overlap`和`top_k`值范围。\n",
        "*   **不同查询：** 使用不同类型的查询（例如，基于事实、比较、摘要）测试相同配置，以查看性能如何变化。\n",
        "*   **更大/不同语料库：** 使用更广泛或特定领域的知识库。\n",
        "*   **实现真正的重新排序：** 用专用的交叉编码器重新排序模型（例如，来自Hugging Face Transformers或Cohere Rerank）替换模拟重新排序，以根据相关性重新评分最初检索的文档。\n",
        "*   **替代模型：** 尝试不同的Nebius AI模型进行嵌入、生成或评估，以查看它们的影响。\n",
        "*   **高级分块：** 探索更复杂的分块策略（例如，递归字符分割、语义分块）。\n",
        "*   **人工评估：** 用人工判断补充自动化指标，以更细致地理解答案质量。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "### 注意\n",
        "\n",
        "**本文档是原始英文RAG实验notebook的中文翻译版本。**\n",
        "\n",
        "**主要功能包括：**\n",
        "\n",
        "1. **RAG系统配置实验** - 测试不同的文本分块大小、重叠设置和检索数量\n",
        "2. **多种RAG策略** - 简单RAG、查询重写RAG、重新排序RAG的比较 \n",
        "3. **自动评估** - 使用忠实度、相关性和语义相似性三个指标\n",
        "4. **Nebius AI集成** - 使用Nebius AI的嵌入、生成和评估模型\n",
        "5. **结果分析** - 通过Pandas进行结果整理和可视化\n",
        "\n",
        "**使用方法：**\n",
        "1. 设置Nebius API密钥\n",
        "2. 配置实验参数\n",
        "3. 运行完整的实验流水线\n",
        "4. 分析结果找到最佳配置\n",
        "\n",
        "**完整的代码实现请参考原始英文版本的notebook文件。**\n"
      ]
    }
  ],
  "metadata": {
    "language_info": {
      "name": "python"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}
